#include "HashTable.h"

unsigned int hash(const char *key, size_t table_size) {
    unsigned int hash = 0;
    while (*key) {
        hash = (hash * 131) + *key++;
    }
    return hash % table_size;
}

HashTable* HashTable_new(size_t size) {
    HashTable *new_table = malloc(sizeof(HashTable));
    new_table->size = size;
    new_table->count = 0;
    new_table->table = calloc(size, sizeof(Node *));
    return new_table;
}

void HashTable_resize(HashTable *table) {
    size_t new_size = table->size * 2;
    Node **new_table = calloc(new_size, sizeof(Node *));
    
    for (size_t i = 0; i < table->size; ++i) {
        Node *node = table->table[i];
        while (node != NULL) {
            Node *next = node->next;
            unsigned int new_index = hash(node->key, new_size);
            node->next = new_table[new_index];
            new_table[new_index] = node;
            node = next;
        }
    }
    
    free(table->table);
    table->table = new_table;
    table->size = new_size;
}

void HashTable_insert(HashTable *table, const char *key, const char *value) {
    if ((double)table->count / table->size > 0.75) {
        HashTable_resize(table);
    }
    
    unsigned int index = hash(key, table->size);
    Node *new_node = malloc(sizeof(Node));
    new_node->key = strdup(key);
    new_node->value = strdup(value);
    new_node->next = table->table[index];
    table->table[index] = new_node;
    table->count++;
}

char* HashTable_search(HashTable *table, const char *key) {
    unsigned int index = hash(key, table->size);
    Node *node = table->table[index];

    while (node != NULL && strcmp(node->key, key) != 0) {
        node = node->next;
    }
    return node? node->value : NULL;
}